/*
 * GLUT IMPLEMENTATION OF GRAPHIC ENGINE
 */
#include <iostream>
#include <vector>
#include <GL/gl.h>
#include <GL/glu.h>
#include <App.h>
#include <AppView.hpp>
#include "../Model/AppList.h"
#include "graphicEngine.h"
#include "glutEngine.h"

#define FRONT 1
#define BACK 0
void GraphicEngine::init(int* argc, char ** argv) {
  /* get access to the current displayed apps (local save) */
  _leftApp = _appList->getLeftApp();
  _middleApp = _appList->getMiddleApp();
  _rightApp = _appList->getRightApp();

  /* Open the window */
  GlutEngine::init(this, _appList, argc, argv);


  /* Blending Function For Translucency Based On Source Alpha Value  */
  //  glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}

GraphicEngine::GraphicEngine(float velocity, float translate,
    float lateralBackTranslate, float lateralHorizontalTranslate,
    float rotation, int xRes, int yRes, int colorDepth, AppList* workingList,
    int* argc, char ** argv) :
  _velocity(velocity),
  _backTranslate(translate),
  _lateralTranslate(lateralBackTranslate),
  _leftAndRightTranslate(lateralHorizontalTranslate),
  _leftAndRightRotate(rotation),
  _sessionActive(false),
  _xRes(xRes),
  _yRes(yRes),
  _colorDepth(colorDepth),
  _replacingApp(NULL),
  _appList(workingList){
  init(argc, argv);
}

GraphicEngine::GraphicEngine(SystemPreferences prefs, AppList * workingList,
    int * argc, char ** argv) :
  _velocity(prefs.getVelocity()),
  _backTranslate(prefs.getBackTranslate()),
  _lateralTranslate(prefs.getLateralBackTranslate()),
  _leftAndRightTranslate(prefs.getLateralDelta()),
  _leftAndRightRotate(prefs.getLateralRotate()),
  _sessionActive(false),
  _xRes(prefs.getXRes()),
  _yRes(prefs.getYRes()),
  _colorDepth(prefs.getDepthColor()),
  _replacingApp(NULL),
  _appList(workingList){
  init(argc, argv);
}

GraphicEngine::~GraphicEngine() {

}

void GraphicEngine::start() {
  GlutEngine::start();
}

void GraphicEngine::refresh() {
  GlutEngine::refresh();
}

void GraphicEngine::draw() {
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  //LEFT app
  drawApp(_leftAndRightTranslate, 0, _lateralTranslate, _leftAndRightRotate, 0,
	  1, 0, _leftApp,BACK);

  //CENTER app
  drawApp(0, 0, _backTranslate, 0, 0, 0, 0, _middleApp,FRONT);

  //RIGHT app
  drawApp(-_leftAndRightTranslate, 0, _lateralTranslate, -_leftAndRightRotate,
	  0, 1, 0, _rightApp,BACK);

  /* reflection section */
  glPushMatrix();
  glScalef(1, -1, 1);
  //LEFT app
  drawApp(_leftAndRightTranslate, 0, _lateralTranslate, _leftAndRightRotate, 0,
	  1, 0, _leftApp,BACK);

  //CENTER app
  drawApp(0, 0, _backTranslate, 0, 0, 0, 0, _middleApp,FRONT);

  //RIGHT app
  drawApp(-_leftAndRightTranslate, 0, _lateralTranslate, -_leftAndRightRotate,
	  0, 1, 0, _rightApp,BACK);
  glPopMatrix();
  drawFloor();
}

void GraphicEngine::rotateLeft() {
  _replacingApp = _appList->getNextRightApp();
  _time = GlutEngine::getTime();
  GlutEngine::state = WindowEngine::ROTATING_LEFT;
  GlutEngine::mask = true;
}

void GraphicEngine::rotateRight() {
  _replacingApp = _appList->getNextLeftApp();
  _time = GlutEngine::getTime();
  GlutEngine::state = WindowEngine::ROTATING_RIGHT;
  GlutEngine::mask = true;
}

void GraphicEngine::drawRotateLeft() {
  int elapsed_time = GlutEngine::getTime() - _time;
  float current_coef = (float) elapsed_time / (float) _velocity;
  bool end = current_coef >= 1.0f;

  if (end)
    current_coef = 1.0f;

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  //LEFT that disapear
  drawApp(_leftAndRightTranslate, 0, _lateralTranslate + _lateralTranslate
	  * current_coef, _leftAndRightRotate, 0, 1, 0, _leftApp,BACK);

  //CENTER that comes to LEFT
  drawApp(_leftAndRightTranslate * current_coef, 0, _lateralTranslate
      * current_coef + _backTranslate * (1 - current_coef), _leftAndRightRotate
	  * current_coef, 0, 1, 0, _middleApp,FRONT);

  //RIGHT that comes to CENTER
  drawApp(-_leftAndRightTranslate * (1 - current_coef), 0, _lateralTranslate
      * (1 - current_coef) + _backTranslate * current_coef,
      -_leftAndRightRotate + _leftAndRightRotate * current_coef, 0, 1, 0,
	  _rightApp,FRONT);

  //NEW that comes to RIGHT
  drawApp(-_leftAndRightTranslate, 0, _lateralTranslate + _lateralTranslate
	  * (1 - current_coef), -_leftAndRightRotate, 0, 1, 0, _replacingApp,BACK);

  /* reflection section */
  glPushMatrix();
  glScalef(1, -1, 1);

  //LEFT that disapear
  drawApp(_leftAndRightTranslate, 0, _lateralTranslate + _lateralTranslate
	  * current_coef, _leftAndRightRotate, 0, 1, 0, _leftApp,BACK);

  //CENTER that comes to LEFT
  drawApp(_leftAndRightTranslate * current_coef, 0, _lateralTranslate
      * current_coef + _backTranslate * (1 - current_coef), _leftAndRightRotate
	  * current_coef, 0, 1, 0, _middleApp,FRONT);

  //RIGHT that comes to CENTER
  drawApp(-_leftAndRightTranslate * (1 - current_coef), 0, _lateralTranslate
      * (1 - current_coef) + _backTranslate * current_coef,
      -_leftAndRightRotate + _leftAndRightRotate * current_coef, 0, 1, 0,
	  _rightApp,FRONT);

  //NEW that comes to RIGHT
  drawApp(-_leftAndRightTranslate, 0, _lateralTranslate + _lateralTranslate
	  * (1 - current_coef), -_leftAndRightRotate, 0, 1, 0, _replacingApp,BACK);
  glPopMatrix();
  drawFloor();

  if (end) {
    _leftApp = _middleApp;
    _middleApp = _rightApp;
    _rightApp = _replacingApp;
    _replacingApp = NULL;

    GlutEngine::state = WindowEngine::IN_APP;
    GlutEngine::mask = false;
  }
  refresh();
}

void GraphicEngine::drawRotateRight() {
  int elapsed_time = GlutEngine::getTime() - _time;
  float current_coef = (float) elapsed_time / (float) _velocity;
  bool end = current_coef >= 1.0f;

  if (end)
    current_coef = 1.0f;

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  //NEW that comes to LEFT
  drawApp(_leftAndRightTranslate, 0, _lateralTranslate + _lateralTranslate * (1
									      - current_coef), _leftAndRightRotate, 0, 1, 0, _replacingApp,BACK);

  //LEFT that comes to center
  drawApp(_leftAndRightTranslate - _leftAndRightTranslate * current_coef, 0,
      _lateralTranslate * (1 - current_coef) + _backTranslate * current_coef,
	  _leftAndRightRotate * (1 - current_coef), 0, 1, 0, _leftApp,FRONT);

  //CENTER that comes to right
  drawApp(-_leftAndRightTranslate * current_coef, 0, _lateralTranslate
      * current_coef + _backTranslate * (1 - current_coef),
	  -_leftAndRightRotate * current_coef, 0, 1, 0, _middleApp,FRONT);

  //RIGHT that desapear
  drawApp(-_leftAndRightTranslate, 0, _lateralTranslate + _lateralTranslate
	  * current_coef, -_leftAndRightRotate, 0, 1, 0, _rightApp,BACK);

  /* reflection section */
  glPushMatrix();
  glScalef(1, -1, 1);
  //NEW that comes to LEFT
  drawApp(_leftAndRightTranslate, 0, _lateralTranslate + _lateralTranslate * (1
									      - current_coef), _leftAndRightRotate, 0, 1, 0, _replacingApp,BACK);

  //LEFT that comes to center
  drawApp(_leftAndRightTranslate - _leftAndRightTranslate * current_coef, 0,
      _lateralTranslate * (1 - current_coef) + _backTranslate * current_coef,
	  _leftAndRightRotate * (1 - current_coef), 0, 1, 0, _leftApp,FRONT);

  //CENTER that comes to right
  drawApp(-_leftAndRightTranslate * current_coef, 0, _lateralTranslate
      * current_coef + _backTranslate * (1 - current_coef),
	  -_leftAndRightRotate * current_coef, 0, 1, 0, _middleApp,FRONT);

  //RIGHT that desapear
  drawApp(-_leftAndRightTranslate, 0, _lateralTranslate + _lateralTranslate
	  * current_coef, -_leftAndRightRotate, 0, 1, 0, _rightApp,BACK);
  glPopMatrix();
  drawFloor();

  if (end) {
    _rightApp = _middleApp;
    _middleApp = _leftApp;
    _leftApp = _replacingApp;
    _replacingApp = NULL;

    GlutEngine::state = WindowEngine::IN_APP;
    GlutEngine::mask = false;
  }
  refresh();
}

bool GraphicEngine::sessionState() {
  return _sessionActive;
}

void GraphicEngine::activateSession() {
  _sessionActive = true;
}

void GraphicEngine::desactivateSession() {
  _sessionActive = false;
}

void GraphicEngine::drawDock() {
  glEnable(GL_BLEND);
  glDisable(GL_DEPTH_TEST);
  if (sessionState()) {
   // glRasterPos2f(40, 40);
   // GlutEngine::drawText("test");
    glPushMatrix();
    glTranslated(0.0, 0.0, 2.1);
    glBegin(GL_QUADS);
    glColor4ub(255, 255, 255, 128);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glVertex3d(-1.0, 0.0, 0.0);
    glVertex3d(1.0, 0.0, 0.0);
    glVertex3d(1.0, 0.25, 0.0);
    glVertex3d(-1.0, 0.25, 0.0);
    glEnd();
    glPopMatrix();
  }
  glDisable(GL_BLEND);
  glEnable(GL_DEPTH_TEST);
}

void GraphicEngine::commonGL() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  /* init OpenGL */
  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_SMOOTH);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45, (float) _xRes / (float) _yRes, 1, 20);
  gluLookAt(0, 1, 0, 0, 1, 30, 0, 1, 0);
  glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR);

}

int GraphicEngine::getXRes() {
  return _xRes;
}

int GraphicEngine::getYRes() {
  return _yRes;
}

/*
 * private section
 */
void GraphicEngine::drawApp(float x_translate, float y_translate,
			    float z_translate, float delta_rotate, 
			    float axis_x_rotate,
			    float axis_y_rotate, float axis_z_rotate, 
			    App* appToDraw, int state) {
  glPushMatrix();
  glTranslated(x_translate, y_translate, z_translate);
  glRotated(delta_rotate, axis_x_rotate, axis_y_rotate, axis_z_rotate);
  if(state == FRONT)
    appToDraw->getView()->displayFront();
  else
    appToDraw->getView()->displayBack();
  glPopMatrix();
}

void GraphicEngine::drawFloor() {
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glBegin(GL_QUADS);
  glColor4f(0.0, 0.0, 0.0, 0.1);
  glVertex3f(-18.0, 0.0, 6.0);
  glVertex3f(27.0, 0.0, 6.0);

  glColor4f(0.0, 0.0, 0.0, 1.);
  glVertex3f(27.0, 0.0, 2.2);
  glVertex3f(-18.0, 0.0, 2.2);
  glEnd();

  glDisable(GL_BLEND);
}
